package keer.httpinterface.utils;

import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;

/**
 * Http接口工厂类，用于加载和管理Http接口的工厂类
 * 该类通过读取类路径下META-INF/httpinterface.factories文件中的工厂配置来发现和实例化工厂类
 *
 * @author Keer
 * @since 1.0.0
 */
@SuppressWarnings("unchecked")
public final class HttpInterfaceFactories {
    // 工厂配置文件名
    private final static String FACTORY_FILE_NAME = "META-INF/httpinterface.factories";
    // 缓存Map，用于存储类加载器与工厂类名列表的映射
    private final static CacheMap<ClassLoader, Map<String, List<String>>> CACHE_MAP = new CacheMap<>();

    // 私有构造方法，防止实例化
    private HttpInterfaceFactories() {

    }

    /**
     * 加载工厂类名列表
     *
     * @param loader       类加载器
     * @param factoryClass 工厂类
     * @return 工厂类名列表，如果未找到则返回空列表
     */
    public static List<String> loadFactoryClassNames(ClassLoader loader, Class<?> factoryClass) {
        // 从缓存中加载或创建工厂类名映射
        Map<String, List<String>> map = CACHE_MAP.loadOrCreate(loader, () -> {
            try {
                // 扫描类路径下的工厂配置文件
                List<URL> urls = ClassUtils.scanClasspathFiles(loader, FACTORY_FILE_NAME);
                // 读取配置文件中的属性
                return ClassUtils.readProperties(urls, ConstantUtils.DEFAULT_CHARSET);
            } catch (IOException e) {
                // 如果发生IO异常，抛出运行时异常
                throw new RuntimeException(e);
            }
        });
        // 获取指定工厂类的类名列表，如果未找到则返回空列表
        List<String> list = map.get(factoryClass.getName());
        if (list == null) {
            return Collections.emptyList();
        }
        return Collections.unmodifiableList(list);
    }

    /**
     * 加载工厂类
     *
     * @param loader 类加载器
     * @param clz    工厂类
     * @return 工厂类列表，如果未找到则返回空列表
     * @throws ClassNotFoundException 如果类名无效
     */
    public static <T> List<Class<T>> loadFactories(ClassLoader loader, Class<T> clz) throws ClassNotFoundException {
        List<Class<T>> result = new ArrayList<>();
        // 加载工厂类名列表
        List<String> classNames = loadFactoryClassNames(loader, clz);
        if (classNames != null) {
            for (String className : classNames) {
                // 通过类名加载工厂类
                Class<T> clazz = (Class<T>) Class.forName(className, Boolean.TRUE, loader);
                result.add(clazz);
            }
        }
        return result;
    }

    /**
     * 加载并实例化工厂类
     *
     * @param loader 类加载器
     * @param clz    工厂类
     * @return 工厂实例列表，如果未找到则返回空列表
     * @throws ClassNotFoundException 如果类名无效
     * @throws InstantiationException 如果实例化失败
     * @throws IllegalAccessException 如果访问类失败
     */
    public static <T> List<T> loadFactoryInstances(ClassLoader loader, Class<T> clz) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        List<T> result = new ArrayList<>();
        // 加载工厂类
        for (Class<T> clazz : loadFactories(loader, clz)) {
            // 实例化工厂类
            result.add(clazz.newInstance());
        }
        return result;
    }

    /**
     * 加载并实例化最后一个工厂类
     *
     * @param loader 类加载器
     * @param clz    工厂类
     * @return 最后一个工厂实例，如果未找到则返回null
     * @throws ClassNotFoundException 如果类名无效
     * @throws InstantiationException 如果实例化失败
     * @throws IllegalAccessException 如果访问类失败
     */
    public static <T> T loadLastOneFactoryInstance(ClassLoader loader, Class<T> clz) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        // 加载工厂类名列表
        List<String> classNames = loadFactoryClassNames(loader, clz);
        if (classNames.isEmpty()) {
            return null;
        }
        // 加载并实例化最后一个工厂类
        Class<?> clazz = Class.forName(classNames.get(classNames.size() - 1));
        return (T) clazz.newInstance();
    }

    /**
     * 加载并实例化指定类加载器加载的特定类型最后一个工厂方法对应的类实例
     * 如果找不到对应的类实例，则使用默认类进行实例化
     * 此方法主要用于在特定类型的工厂类中寻找并实例化最后一个工厂方法对应的类
     *
     * @param loader       类加载器，用于加载类
     * @param clz          要加载的类的类型
     * @param defaultClass 默认的类类型，当找不到指定类型的工厂方法时使用
     * @return 返回最后一个工厂方法对应的类实例，如果找不到则返回默认类的实例
     * @throws ClassNotFoundException 如果指定的类无法找到时抛出
     * @throws InstantiationException 当尝试实例化类时，如果类没有空构造方法或者是抽象类、接口时抛出
     * @throws IllegalAccessException 当没有访问权限时抛出
     */
    public static <T> T loadLastOneFactoryInstance(ClassLoader loader, Class<T> clz, Class<T> defaultClass) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        // 尝试加载并实例化指定类型的最后一个工厂方法对应的类实例
        T instance = loadLastOneFactoryInstance(loader, clz);
        // 如果实例化失败，即没有找到对应的工厂方法，则使用默认类进行实例化
        if (instance == null) {
            instance = defaultClass.newInstance();
        }
        // 返回实例化的对象
        return instance;
    }

    /**
     * 加载最后一个工厂实例
     * 此方法尝试通过类加载器加载指定类型的最后一个工厂实例如果找不到合适的实例，它会使用提供的Supplier生成一个新的实例
     *
     * @param loader   类加载器，用于加载指定类型的工厂类
     * @param clz      指定的类型，用于查找工厂类
     * @param supplier 一个提供默认实例的Supplier，当找不到工厂实例时使用
     * @return 返回最后一个工厂实例或者通过Supplier生成的新实例
     * @throws ClassNotFoundException 如果指定类型的工厂类未找到，则抛出此异常
     * @throws InstantiationException 如果由于某种原因（如类不是抽象的并且没有无参数构造函数）无法实例化指定类型的工厂类，则抛出此异常
     * @throws IllegalAccessException 如果对指定类型的工厂类的访问权限不足，则抛出此异常
     */
    public static <T> T loadLastOneFactoryInstance(ClassLoader loader, Class<T> clz, Supplier<T> supplier) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        // 尝试加载最后一个工厂实例
        T instance = loadLastOneFactoryInstance(loader, clz);
        // 如果实例为空，则使用Supplier生成一个新的实例
        if (instance == null) {
            instance = supplier.get();
        }
        // 返回实例
        return instance;
    }

    /**
     * 加载并实例化第一个工厂类
     *
     * @param loader 类加载器
     * @param clz    工厂类
     * @return 第一个工厂实例，如果未找到则返回null
     * @throws ClassNotFoundException 如果类名无效
     * @throws InstantiationException 如果实例化失败
     * @throws IllegalAccessException 如果访问类失败
     */
    public static <T> T loadFirstOneFactoryInstance(ClassLoader loader, Class<T> clz) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        // 加载工厂类名列表
        List<String> classNames = loadFactoryClassNames(loader, clz);
        if (classNames.isEmpty()) {
            return null;
        }
        // 加载并实例化第一个工厂类
        Class<?> clazz = Class.forName(classNames.get(0));
        return (T) clazz.newInstance();
    }

    /**
     * 加载并实例化第一个工厂类实例
     * 此方法尝试从给定类加载器和类类型中加载并实例化一个工厂类的实例如果无法找到合适的实例，
     * 则使用默认类进行实例化
     * 主要用途是为了解决需要动态加载并使用配置或环境变量中指定的工厂类实例的场景，
     * 同时提供一个默认实现以确保系统稳定性
     *
     * @param loader       用于加载类的类加载器，通常为应用程序的类加载器
     * @param clz          工厂类的接口或抽象类类型，用于限定加载的类类型
     * @param defaultClass 默认的工厂类实现类型，当没有找到其他实现时使用
     * @return 返回实例化的工厂类实例
     * @throws ClassNotFoundException 当默认类找不到时抛出
     * @throws InstantiationException 当默认类无法实例化时抛出
     * @throws IllegalAccessException 当默认类的构造方法不可访问时抛出
     */
    public static <T> T loadFirstOneFactoryInstance(ClassLoader loader, Class<T> clz, Class<T> defaultClass) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        // 尝试加载并实例化工厂类的第一个实现
        T instance = loadFirstOneFactoryInstance(loader, clz);
        // 如果没有成功加载到实例，则使用默认类进行实例化
        if (instance == null) {
            instance = defaultClass.newInstance();
        }
        // 返回最终的实例化对象
        return instance;
    }

    /**
     * 加载并实例化第一个工厂类实例
     * 此方法尝试从指定类加载器中加载第一个匹配的工厂类实例如果找不到合适的实例，则通过提供的Supplier创建一个
     *
     * @param loader   类加载器，用于加载工厂类
     * @param clz      工厂类的类型
     * @param supplier 当无法从类加载器中实例化工厂类时，用于创建实例的备用供应商
     * @return 返回第一个工厂类实例，如果无法实例化，则返回null
     * @throws ClassNotFoundException 如果指定的工厂类无法找到
     * @throws InstantiationException 如果由于某种原因实例化失败
     * @throws IllegalAccessException 如果访问工厂类的构造函数时出现问题
     */
    public static <T> T loadFirstOneFactoryInstance(ClassLoader loader, Class<T> clz, Supplier<T> supplier) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        // 尝试加载并实例化第一个工厂类实例
        T instance = loadFirstOneFactoryInstance(loader, clz);
        // 如果实例化失败，则使用备用供应商尝试创建实例
        if (instance == null) {
            instance = supplier.get();
        }
        // 返回最终的实例
        return instance;
    }
}

